#ifndef THREEADDRESSCODE_H_
#define THREEADDRESSCODE_H_

#include <string>
#include <iostream>
#include "TableEntry.h"

#define ADDR_RETURN_VALUE 12

enum OpCode { TAC_ERROR, TAC_GOTO, TAC_IF_FALSE, TAC_ASSIGN, 
	TAC_ASSIGN_TO_ADDRESS_AT, TAC_ADDRESS_OF,
	TAC_NOT,
	/* must be grouped together */
	TAC_MULT, TAC_DIV, TAC_MOD,
	TAC_PLUS, TAC_MINUS, 
	TAC_LT, TAC_GT, TAC_EQ, TAC_LTE, TAC_GTE, TAC_NOT_EQ,
	TAC_AND, TAC_OR, 
	/* end arithmetic operations group */
	TAC_CALL, TAC_HALT, TAC_PARAM, TAC_NUM_CONST, TAC_CHAR_CONST, TAC_NO_OP, 
	TAC_READ, TAC_WRITE, TAC_SAVE_RET_ADDR, TAC_SAVE_OLD_TOPSTK, 
	TAC_SET_NEW_TOPSTK, TAC_SAVE_RET_VAL, TAC_RESTORE_RET_ADDR, 
	TAC_RESTORE_OLD_TOPSTK, TAC_GOTO_RETADDR, 
	TAC_SENTRY 
};

class ThreeAddressCode
{
public:
	ThreeAddressCode(OpCode opCode);
	ThreeAddressCode(attr result, attr operand1, OpCode opCode=TAC_ASSIGN);
	ThreeAddressCode(std::string gotoLabel, OpCode opCode=TAC_GOTO);
	ThreeAddressCode(attr result, attr operand1, attr operand2, OpCode opCode);
	ThreeAddressCode(attr operand, std::string targetLabel, OpCode opCode=TAC_IF_FALSE);
	ThreeAddressCode(attr result, std::string label, int paramCount, OpCode opCode=TAC_CALL);
	ThreeAddressCode(attr result, int index, OpCode opCode);
	ThreeAddressCode(attr operand, OpCode opCode=TAC_PARAM); 
	virtual ~ThreeAddressCode();
	
	void setLabel(std::string label);
	bool hasLabel() const;
	std::string getLabel() const;

	std::string getGotoLabel() const;	
	OpCode getOpCode() const;
	
	int getOperand1() const;
	int getOperand2() const;
	int getResult() const;
	
	bool isOperand1aParam() const;
	bool isOperand2aParam() const;
	bool isResultParam() const;
	
	int getConstIndex() const;
	
	virtual std::ostream& inserter(std::ostream &os) const;
	
private: 
	OpCode _operation;
	
	attr _result;
	attr _operand1;
	attr _operand2;

	int _parameterCount;
	int _constIndex;
	
	std::string _label;
	std::string _gotoLabel;
	
	static std::string _opCodeToString[TAC_SENTRY];
	
	void initTAC();
};

/**
 * Insertion operator overloading for ThreeAddressCodes
 */
inline std::ostream& operator << (std::ostream &os, const ThreeAddressCode &tac) {
	return tac.inserter(os);
}

#endif /*THREEADDRESSCODE_H_*/
